package com.ys.http.download;

import android.util.Log;

import com.ys.http.api.DownLoadApi;
import com.ys.http.network.HttpHelper;

import java.io.File;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.Okio;

/***
 * 文件下载方法
 * 链式调用
 *new DownloadTask.Builder()
 * setDownloadUrl
 * setSaveUrl
 * setOnDownloadListener
 *  .build();
 *   mTask.start();
 *
 *   停止方法
 *   mTask.stop();
 *
 */
public class DownloadTask {

    public static final int STATE_IDLE = 0;
    public static final int STATE_DOWNLOADING = 1;
    public static final int STATE_ERROR = 2;
    public static final int STATE_COMPLETE = 3;

    private static final String TAG = "DownloadTask";
    private final OnDownloadListener mDownloadListener;
    private String mDownloadUrl;
    private String mSavePath;

    private final DownLoadApi mDownApi;

    private Disposable mDisposable;
    private Disposable mProgressDisposable;
    private long mCurrentSize; //当前已读写的字节数
    private long mSecondSize; //上一秒读取的总字节数
    private long mSpeedSize;  //每秒读取的字节数
    private long mTotalSize;
    private int state;

    private DownloadTask(String downloadUrl, String savePath, OnDownloadListener listener) {
        this.mDownloadUrl = downloadUrl;
        this.mSavePath = savePath;
        this.mDownloadListener = listener == null ? new DefaultDownloadListener() : listener;
        mDownApi = HttpHelper.getInstance().createApi(DownLoadApi.class);
    }

    /**
     * 开始下载
     */
    public void start() {
        if (mDownloadUrl == null || mSavePath == null) {
            Log.e(TAG, "retrofit is null or mDownloadUrl is null or mSaveUrl is null");
            return;
        }
        if (state == STATE_DOWNLOADING) {
            return;
        }
        stop();
        state = STATE_DOWNLOADING;
        mDisposable = mDownApi.download(mDownloadUrl)
                .doOnNext(this::writeToFile)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(body -> {
                    int progress = 100;
                    state = STATE_COMPLETE;
                    mDownloadListener.onProgress(progress, mSpeedSize);
                    mDownloadListener.onSuccess(mSavePath);
                }, throwable -> {
                    stop();
                    state = STATE_ERROR;
                    mDownloadListener.onFailure(throwable);
                });
    }

    public void stop() {
        if (mDisposable != null) {
            mDisposable.dispose();
        }
        if (mProgressDisposable != null) {
            mProgressDisposable.dispose();
        }
        state = STATE_IDLE;
        mDisposable = null;
        mProgressDisposable = null;
    }

    private void initDownloadProgress() {
        mProgressDisposable = Observable.interval(1, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(val -> {
                    int progress = 0;
                    if (mTotalSize > 0) {
                        progress = (int) (mCurrentSize * 100 / mTotalSize);
                    }
                    mSpeedSize = mCurrentSize - mSecondSize;
                    mSecondSize += mSpeedSize;
                    mDownloadListener.onProgress(progress, mSpeedSize);
                });
    }

    /**
     * 将输入流写入文件
     *
     * @param body
     */
    private void writeToFile(ResponseBody body) throws Exception {
        int index = mSavePath.lastIndexOf(File.separator);
        String pathDir = mSavePath.substring(0, index);
        File fileDir = new File(pathDir);
        if (!fileDir.isDirectory()) {
            fileDir.mkdirs();
        }
        File file = new File(mSavePath);

        mTotalSize = body.contentLength();
        initDownloadProgress();

        try (BufferedSource source = body.source();
             BufferedSink sink = Okio.buffer(Okio.sink(file)))
        {
            byte[] size = new byte[8192];
            int len;
            while ((len = source.read(size)) != -1) {
                mCurrentSize += len;
                sink.write(size, 0, len);
            }
            sink.flush();
            mProgressDisposable.dispose();
        }
    }

    public long getContentSize() {
        return mTotalSize;
    }

    public int getState() {
        return state;
    }

    public void setSavePath(String path) {
        this.mSavePath = path;
    }

    public void setDownloadUrl(String url) {
        this.mDownloadUrl = url;
    }

    public String getPath() {
        return mSavePath;
    }

    public String getUrl() {
        return mDownloadUrl;
    }

    public static class Builder {
        private OnDownloadListener listener;
        private String downloadUrl;
        private String savePath;

        public Builder setSavePath(String path) {
            this.savePath = path;
            return this;
        }

        public Builder setDownloadUrl(String downloadUrl) {
            this.downloadUrl = downloadUrl;
            return this;
        }

        public Builder setOnDownloadListener(OnDownloadListener listener) {
            this.listener = listener;
            return this;
        }

        public DownloadTask build() {
            return new DownloadTask(downloadUrl, savePath, listener);
        }
    }

}
